package com.pb.wkflow.core.service.impl;

import com.pb.wkflow.api.controller.response.TaskResponse;
import com.pb.wkflow.api.exception.ApprovalException;
import com.pb.wkflow.api.service.WfSysProcInfoService;
import com.pb.wkflow.core.camunda.bpmn.instance.Participants;
import com.pb.wkflow.core.config.Constants;
import com.pb.wkflow.core.config.DeleteReasonConstants;
import com.pb.wkflow.core.entity.PostInfo;
import com.pb.wkflow.core.service.*;
import com.pb.wkflow.core.utils.AssertUtils;
import com.pb.wkflow.core.utils.StringUtils;
import com.pb.wkflow.core.utils.WorkflowThreadLocalUtil;
import org.apache.commons.collections.CollectionUtils;
import org.camunda.bpm.engine.ProcessEngine;
import org.camunda.bpm.engine.RuntimeService;
import org.camunda.bpm.engine.history.HistoricActivityInstance;
import org.camunda.bpm.engine.impl.ProcessInstanceModificationBuilderImpl;
import org.camunda.bpm.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.camunda.bpm.engine.impl.interceptor.CommandExecutor;
import org.camunda.bpm.engine.impl.persistence.entity.HistoricTaskInstanceEntity;
import org.camunda.bpm.engine.task.Task;
import org.camunda.bpm.model.bpmn.BpmnModelInstance;
import org.camunda.bpm.model.bpmn.instance.UserTask;
import org.camunda.bpm.model.xml.instance.ModelElementInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author 苗益辉
 * @date 2022/6/20 21:28
 */
@Service
public class WorkflowEngineServiceImpl implements WorkFlowEngineService {

    /** 开始节点 */
    private static final String START_EVENT = "startEvent";

    private static final String MULTI_INSTANCE_BODY = "multiInstanceBody";
    /** 结束节点 */
    private static final String END_EVENT = "noneEndEvent";
    /** 用户节点 */
    private static final String USER_TASK = "userTask";
    /** 服务节点 */
    private static final String SERVICE_TASK = "serviceTask";
    /** 连接线 */
    private static final String SEQUENCE_FLOW = "sequenceFlow";
    /** 排他网关 */
    private static final String EXCLUSIVE_GATEWAY = "exclusiveGateway";
    /** 包容网关 */
    private static final String INCLUSIVE_GATEWAY = "inclusiveGateway";
    /** 并行网关 */
    private static final String PARALLEL_GATEWAY = "parallelGateway";

    @Autowired
    WorkflowTaskService workflowTaskService;

    @Autowired
    RuntimeService runtimeService;

    @Autowired
    WorkflowDefinitionService workflowDefinitionService;

    @Autowired
    WfSysProcInfoService wfSysProcInfoService;

    @Autowired
    WorkflowInstanceService workflowInstanceService;

    @Autowired
    WorkflowBpmnService workflowBpmnService;

    @Autowired
    ProcessEngine processEngine;

    @Autowired
    UserParticipantService userParticipantService;

    @Override
    public void terminateProcess(String processInstanceId, String comment, String taskId, String userId) {
        //默认deleteReason为终止
        if(StringUtils.isBlank(comment)) {
            comment = DeleteReasonConstants.TERMINATE;
        }
        if(StringUtils.isBlank(taskId)) {
            List<Task> list = workflowTaskService.findTaskList(processInstanceId);
            if(list != null) {
                for(Task task : list) {
                    workflowTaskService.setTaskComment(task.getId(), comment, processInstanceId);
                    if(StringUtils.isNotBlank(userId)) {
                        workflowTaskService.setAssignee(task.getId(), userId);
                    }
                }
            }
        }else {
            workflowTaskService.setTaskComment(taskId, comment, processInstanceId);
            if(StringUtils.isNotBlank(userId)) {
                workflowTaskService.setAssignee(taskId, userId);
            }
        }
        workflowTaskService.deleteProcessInstance(processInstanceId, DeleteReasonConstants.TERMINATE);
    }

    @Override
    public void refuseProcess(String processInstanceId, String comment, String taskId, String userId) {
        //默认deleteReason为终止
        if(StringUtils.isBlank(comment)) {
            comment = DeleteReasonConstants.REFUSE;
        }
        if(StringUtils.isBlank(taskId)) {
            List<Task> list = workflowTaskService.findTaskList(processInstanceId);
            if(list != null) {
                for(Task task : list) {
                    workflowTaskService.setTaskComment(task.getId(), comment, processInstanceId);
                    if(StringUtils.isNotBlank(userId)) {
                        workflowTaskService.setAssignee(task.getId(), userId);
                    }
                }
            }
        }else {
            workflowTaskService.setTaskComment(taskId, comment, processInstanceId);
            if(StringUtils.isNotBlank(userId)) {
                workflowTaskService.setAssignee(taskId, userId);
            }
        }
        workflowTaskService.deleteProcessInstance(processInstanceId, DeleteReasonConstants.REFUSE);
    }

    /**
     * 审批通过
     * @param taskId 任务id
     * @param variables 流程变量
     */
    @Override
    public void passProcess(String taskId, Map<String, Object> variables) {
//        List<Task> taskList =
    }

    @Override
    public void backTask(TaskResponse response, String processStatus) {
        String activityId = response.getActivityId();
        String taskId = response.getTaskId();
        String comment = response.getComment();
        AssertUtils.notEmpty(activityId, "回退失败，目标节点id为空");
        WorkflowThreadLocalUtil.put("deleteReasonProcInstId", response.getProcessInstanceId());
        WorkflowThreadLocalUtil.put("deleteReasonTaskId", response.getTaskId());
        WorkflowThreadLocalUtil.put("deleteReason", processStatus);
        if (StringUtils.isNotBlank(comment)) {
            workflowTaskService.setTaskComment(taskId, comment, response.getProcessInstanceId());
        }
        // 退回人
        String userId = response.getUserId();
        if (StringUtils.isNotBlank(userId)) {
            workflowTaskService.setAssignee(taskId, userId);
        }
        //流程变量
        if (response.getVariables() != null) {
            workflowTaskService.setTaskVariables(taskId, response.getVariables());
        }
        //任务本地变量
        if (response.getLocalVariables() != null) {
            workflowTaskService.setTaskVariableLocal(taskId, response.getLocalVariables());
        }
        ProcessInstanceModificationBuilderImpl processInstanceModification = (ProcessInstanceModificationBuilderImpl)
                        runtimeService.createProcessInstanceModification(response.getProcessInstanceId());
        if (CollectionUtils.isNotEmpty(response.getCancelActivityIds())) {
            //并行节点要取消并行其他分支的兄弟节点
            for (String cancelActivityId : response.getCancelActivityIds()) {
                processInstanceModification.cancelAllForActivity(cancelActivityId);
            }
        } else {
            processInstanceModification.cancelAllForActivity(activityId);
        }
        List<String> nextActivityIds = response.getNextActivityIds();
        Task task = workflowTaskService.findTask(taskId);
        BpmnModelInstance bpmnModelInstance = workflowDefinitionService.findModel(task.getProcessDefinitionId());
        for (String nextActivityId : nextActivityIds) {
            ModelElementInstance modelElementInstance = bpmnModelInstance.getModelElementById(nextActivityId);
            if (modelElementInstance instanceof UserTask) {
                UserTask userTask = (UserTask) modelElementInstance;
                if (userTask.getLoopCharacteristics() != null) {
                    //如果是会签
                    nextActivityId = nextActivityId + "#multiInstanceBody";
                }
            }
            processInstanceModification.startBeforeActivity(nextActivityId);
        }
        processInstanceModification.execute();
    }

    @Override
    public void reBackTask(TaskResponse response) {
        List<HistoricActivityInstance> activityInstances = workflowInstanceService
                .queryHistoryServiceActivityInstanceAfter(response.getProcessInstanceId(), response.getStartTime());

        ProcessInstanceModificationBuilderImpl processInstanceModification = (ProcessInstanceModificationBuilderImpl)
                runtimeService.createProcessInstanceModification(response.getProcessInstanceId());
        processInstanceModification.setModificationReason(DeleteReasonConstants.REBACK);
        List<String> reBackIds = new ArrayList<>();
        for (HistoricActivityInstance historicActivityInstance : activityInstances) {
            //去掉撤回节点本身
            if (response.getTaskId().equals(historicActivityInstance.getTaskId())) {
                continue;
            }
            String activityType = historicActivityInstance.getActivityType();
            //去掉开始节点、会签节点、排他网关、并行网关、结束节点
            if (START_EVENT.equals(activityType) || MULTI_INSTANCE_BODY.equals(activityType)
                    || EXCLUSIVE_GATEWAY.equals(activityType) || PARALLEL_GATEWAY.equals(activityType)
                    || END_EVENT.equals(activityType)) {
                continue;
            }
            // 如果是子流程
            if ("callActivity".equals(activityType)) {
                processInstanceModification.cancelActivityInstance(historicActivityInstance.getId());
                //获取子流程的实例
                List<HistoricActivityInstance> callActivityInstances = workflowInstanceService
                        .queryHistoryServiceActivityInstance(historicActivityInstance.getCalledProcessInstanceId());
                for (HistoricActivityInstance callActivityInstance : callActivityInstances) {
                    reBackIds.add(callActivityInstance.getTaskId());
                    if (USER_TASK.equals(callActivityInstance.getActivityType()) && callActivityInstance.getEndTime() != null) {
                        throw new ApprovalException(Constants.ProcessMsg.REBACK_ERROR_TASK_HASDONE);
                    }
                }
            } else {
                AssertUtils.notTrue(historicActivityInstance.getEndTime() == null, Constants.ProcessMsg.REBACK_ERROR_TASK_HASDONE);
                reBackIds.add(historicActivityInstance.getTaskId());
                processInstanceModification.cancelActivityInstance(historicActivityInstance.getId());
            }
            if (USER_TASK.equals(historicActivityInstance.getActivityType()) && historicActivityInstance.getEndTime() != null) {
                throw new ApprovalException(Constants.ProcessMsg.REBACK_ERROR_TASK_HASDONE);
            }
        }
        WorkflowThreadLocalUtil.put("reBack", reBackIds);
        processInstanceModification.startBeforeActivity(response.getActivityId());
        processInstanceModification.execute();

        //调用撤回接口的节点
        ProcessEngineConfigurationImpl configuration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        CommandExecutor commandExecutor = configuration.getCommandExecutorTxRequired();
        commandExecutor.execute(commandContext -> {
            HistoricTaskInstanceEntity historicTaskInstance = commandContext.getDbSqlSession()
                    .selectById(HistoricTaskInstanceEntity.class, response.getTaskId());
            if (historicTaskInstance != null) {
                historicTaskInstance.setDeleteReason(DeleteReasonConstants.REBACK);
            }
            return null;
        });
    }

    @Override
    public void transferAssignee(TaskResponse response) {
        String taskId = response.getTaskId();
        PostInfo postInfo = response.getTransferUser();
        List<PostInfo> postInfos = new ArrayList<>();
        postInfos.add(postInfo);
        userParticipantService.deleteRuIdentityLink(taskId);
        userParticipantService.saveUserTaskAssignment(response, postInfos);
        if (postInfos.size() == 1 && Participants.TYPE_USER.equals(postInfos.get(0).getType())) {
                workflowTaskService.setAssignee(taskId, postInfos.get(0).getUserId());
        } else {
            workflowTaskService.setAssignee(taskId, null);
        }
    }

    @Override
    public void transferAssigneeList(TaskResponse response) {
        String taskId = response.getTaskId();
        List<PostInfo> postInfos = response.getTransferUserList();
        userParticipantService.deleteRuIdentityLink(taskId);
        userParticipantService.saveUserTaskAssignment(response, postInfos);
    }
}
